package drds.global_transaction.resource_manager.execute;

import drds.common.$;
import drds.common.Constants;
import drds.global_transaction.api.global_transaction_id_context.GlobalTransactionIdContexts;
import drds.global_transaction.resource_manager.execute.dml.DeleteExecutor;
import drds.global_transaction.resource_manager.execute.dml.InsertExecutor;
import drds.global_transaction.resource_manager.execute.dml.UpdateExecutor;
import drds.global_transaction.resource_manager.proxy.resource_manager.StatementProxy;
import drds.global_transaction.resource_manager.sql_recognizer.SqlRecognizer;
import drds.global_transaction.resource_manager.sql_recognizer.SqlVisitorFactory;
import lombok.extern.slf4j.Slf4j;

import java.sql.SQLException;
import java.sql.Statement;

@Slf4j
public class ExecuteTemplate
{

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <ResultObject, S extends Statement> ResultObject execute(SqlRecognizer sqlRecognizer, StatementProxy<S> statementProxy, StatementExecuteTemplate<ResultObject, S> statementExecuteTemplate, Object... args) throws SQLException
	{

		if (!GlobalTransactionIdContexts.inGlobalTransaction())
		{
			// Just work as original statement
			return statementExecuteTemplate.execute(statementProxy.getStatement(), args);
		}
		if (sqlRecognizer == null)
		{
			sqlRecognizer = SqlVisitorFactory.get(statementProxy.getSql());
		}
		Executor<ResultObject> executor = null;
		switch (sqlRecognizer.getSqlType())
		{
		case insert:
			executor = new InsertExecutor<ResultObject, S>(sqlRecognizer, statementProxy, statementExecuteTemplate);
			break;
		case update:
			executor = new UpdateExecutor<ResultObject, S>(sqlRecognizer, statementProxy, statementExecuteTemplate);
			break;
		case delete:
			executor = new DeleteExecutor<ResultObject, S>(sqlRecognizer, statementProxy, statementExecuteTemplate);
			break;
		case select_for_update:
			executor = new SelectForUpdateExecutor(sqlRecognizer, statementProxy, statementExecuteTemplate);
			break;
		default:
			executor = (Executor<ResultObject>) new PlainExecutor<ResultObject, S>(sqlRecognizer, statementProxy, statementExecuteTemplate);
			break;
		}
		ResultObject resultObject = null;
		try
		{
			resultObject = executor.execute(args);
		} catch (Exception e)
		{
			if (Constants.developMode)
			{
				e.printStackTrace();
			}
			log.error($.printStackTraceToString(e));
			if (e instanceof SQLException)
			{
				throw (SQLException) e;
			} else
			{
				// Turn everything into SQLException
				throw new SQLException(e);
			}
		}
		return resultObject;
	}

	public static <T, S extends Statement> T execute(StatementProxy<S> statementProxy, StatementExecuteTemplate<T, S> statementExecuteTemplate, Object... args) throws SQLException
	{
		return execute(null, statementProxy, statementExecuteTemplate, args);
	}
}
